package br.com.gscorp.bankLion.security;

import java.util.ArrayList;
import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.authentication.dao.AbstractUserDetailsAuthenticationProvider;
import org.springframework.security.authentication.dao.SaltSource;
import org.springframework.security.authentication.encoding.PasswordEncoder;
import org.springframework.security.authentication.encoding.ShaPasswordEncoder;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;

import br.com.gscorp.bankLion.entity.Usuario;
import br.com.gscorp.bankLion.repository.UsuarioRepository;

@SuppressWarnings("deprecation")
@Component
public class BankLionAuthenticationProvider extends
AbstractUserDetailsAuthenticationProvider {

	@Autowired
	private UsuarioRepository usuarioRepository;

	private ShaPasswordEncoder passwordEncoder = new ShaPasswordEncoder(256);

	private SaltSource saltSource;

	public BankLionAuthenticationProvider() {
		super();
		this.hideUserNotFoundExceptions = false;
	}

	@Override
	protected void additionalAuthenticationChecks(
			final UserDetails userDetails,
			final UsernamePasswordAuthenticationToken authentication)
					throws AuthenticationException {
		Object salt = null;

		if (saltSource != null) {
			salt = saltSource.getSalt(userDetails);
		}

		if (authentication.getCredentials() == null) {
			logger.debug("Authentication failed: no credentials provided");

			throw new BadCredentialsException(messages.getMessage(
					"AbstractUserDetailsAuthenticationProvider.badCredentials",
					"Senha inválida"), userDetails);
		}

		final String presentedPassword = authentication.getCredentials()
				.toString();

		if (!passwordEncoder.isPasswordValid(userDetails.getPassword(),
				presentedPassword, salt)) {
			logger.debug("Authentication failed: password does not match stored value");

			throw new BadCredentialsException(messages.getMessage(
					"AbstractUserDetailsAuthenticationProvider.badCredentials",
					"senha.invalida"), userDetails);
		}
	}

	@Override
	protected final UserDetails retrieveUser(final String name,
			final UsernamePasswordAuthenticationToken authentication) {

		Usuario usuario = usuarioRepository.findByUsuarioIgnoreCase(name);
		if(usuario == null) {
			throw new UsernameNotFoundException("email.invalido");
		}

		String senha = passwordEncoder.encodePassword(authentication.getCredentials().toString(), null);
		System.out.println(senha);
		usuario = usuarioRepository.findByUsuarioAndSenhaIgnoreCase(name, senha);

		if(usuario == null) {
			throw new UsernameNotFoundException("senha.invalida");
		} else if (!usuario.getAtivo()){
			throw new UsernameNotFoundException("usuario.inativo");
		}

		String role = "";
		List<GrantedAuthority> authorityList = new ArrayList<GrantedAuthority>();

		
			role = AuthorityConstant.ROLE_ADMINISTRADOR;
			authorityList = AuthorityUtils.createAuthorityList(role, AuthorityConstant.ROLE_USUARIO);

		return new BankLionUserDetails(usuario, true, true, true, true, authorityList);
	}

	public void setPasswordEncoder(final Object passwordEncoderToSet) {
		Assert.notNull(passwordEncoderToSet, "passwordEncoder cannot be null");

		if (passwordEncoderToSet instanceof PasswordEncoder) {
			passwordEncoder = (ShaPasswordEncoder) passwordEncoderToSet;
			return;
		}

		if (passwordEncoderToSet instanceof org.springframework.security.crypto.password.PasswordEncoder) {
			final org.springframework.security.crypto.password.PasswordEncoder delegate = (org.springframework.security.crypto.password.PasswordEncoder) passwordEncoderToSet;
			passwordEncoder = new ShaPasswordEncoder() {
				@Override
				public String encodePassword(final String rawPass,
						final Object salt) {
					checkSalt(salt);
					return delegate.encode(rawPass);
				}

				@Override
				public boolean isPasswordValid(final String encPass,
						final String rawPass, final Object salt) {
					checkSalt(salt);
					return delegate.matches(rawPass, encPass);
				}

				private void checkSalt(final Object salt) {
					Assert.isNull(salt,
							"Salt value must be null when used with crypto module PasswordEncoder");
				}
			};

			return;
		}

		throw new IllegalArgumentException(
				"passwordEncoder must be a PasswordEncoder instance");
	}

	protected PasswordEncoder getPasswordEncoder() {
		return passwordEncoder;
	}

	public void setSaltSource(final SaltSource saltSourceToSet) {
		saltSource = saltSourceToSet;
	}

	protected SaltSource getSaltSource() {
		return saltSource;
	}

}